#region Copyright (C) 2007 Jens Winter

//Copyright (C) 2007 Jens Winter

//This library is free software; you can redistribute it and/or
//modify it under the terms of the GNU Lesser General Public
//License as published by the Free Software Foundation; either
//version 2.1 of the License, or (at your option) any later version.

//This library is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//Lesser General Public License for more details.

//You should have received a copy of the GNU Lesser General Public
//License along with this library; if not, write to the Free Software
//Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

#endregion

using System;
using System.Data;
using System.Transactions;
using Castle.MicroKernel;
using Flynn.Core.ConnectionFactories;
using Flynn.Core.ExtensionMethods;
using Flynn.Core.TaskResults;

namespace Flynn.Core.Tasks
{
    public class TaskExecutionService : ITaskExecutionService
    {
        private readonly IPrepareDatabaseTask _prepareDatabaseTask;
        private readonly IUpdateDatabaseTask _updateDatabaseTask;
        private readonly ICheckDatabaseTask _checkDatabaseTask;
        private readonly IKernel _kernel;

        public TaskExecutionService(IPrepareDatabaseTask prepareDatabaseTask, IUpdateDatabaseTask updateDatabaseTask, ICheckDatabaseTask checkDatabaseTask, IKernel kernel)
        {
            if (prepareDatabaseTask == null)
                throw new ArgumentNullException("prepareDatabaseTask");

            if (updateDatabaseTask == null)
                throw new ArgumentNullException("updateDatabaseTask");

            if (checkDatabaseTask == null)
                throw new ArgumentNullException("checkDatabaseTask");

            if (kernel == null)
                throw new ArgumentNullException("kernel");

            _prepareDatabaseTask = prepareDatabaseTask;
            _updateDatabaseTask = updateDatabaseTask;
            _checkDatabaseTask = checkDatabaseTask;

            _kernel = kernel;
        }

        public PreparingDatabaseResult ExecutePrepareDatabaseTask(Project project, string connectionString)
        {
            if (project == null)
                throw new ArgumentNullException("project");

            using (TransactionScope transactionScope = new TransactionScope())
            {
                if (!_kernel.HasComponent(project.Database.ToConnectionFactoryKey()))
                    return PreparingDatabaseResult.Failure(string.Format("Database '{0}' is unknown.", project.Database));

                IConnectionFactory connectionFactory = new SafeConnectionFactory((IConnectionFactory)_kernel.Resolve(project.Database.ToConnectionFactoryKey(), typeof(IConnectionFactory)));

                try
                {
                    using (IDbConnection connection = connectionFactory.CreateConnection(connectionString))
                    {
                        connection.Open();

                        PreparingDatabaseResult result = _prepareDatabaseTask.PrepareDatabase(project, connection);

                        if (!result.Failed)
                            transactionScope.Complete();

                        return result;
                    }
                }
                catch (CreatingConnectionException e)
                {
                    return PreparingDatabaseResult.Failure(string.Format("Creating database connection failed ({0})", e.Message));
                }
            }
        }

        public UpdatingDatabaseResult ExecuteUpdateDatabaseTask(Project project, string targetVersion, string connectionString)
        {
            if (project == null)
                throw new ArgumentNullException("project");

            using (TransactionScope transactionScope = new TransactionScope())
            {
                if (!_kernel.HasComponent(project.Database.ToConnectionFactoryKey()))
                    return UpdatingDatabaseResult.Failure(string.Format("Database '{0}' is unknown.", project.Database));

                IConnectionFactory connectionFactory = new SafeConnectionFactory((IConnectionFactory)_kernel.Resolve(project.Database.ToConnectionFactoryKey(), typeof(IConnectionFactory)));

                try
                {
                    using (IDbConnection connection = connectionFactory.CreateConnection(connectionString))
                    {
                        connection.Open();

                        UpdatingDatabaseResult result = _updateDatabaseTask.UpdateDatabase(project, targetVersion, connection);

                        if (!result.Failed)
                            transactionScope.Complete();

                        return result;
                    }
                }
                catch (CreatingConnectionException e)
                {
                    return UpdatingDatabaseResult.Failure(string.Format("Creating database connection failed ({0})", e.Message));
                }
            }
        }

        public CheckingDatabaseResult ExecuteCheckDatabaseTask(Project project, string connectionString)
        {
            if (project == null)
                throw new ArgumentNullException("project");

            using (TransactionScope transactionScope = new TransactionScope())
            {
                if (!_kernel.HasComponent(project.Database.ToConnectionFactoryKey()))
                    return CheckingDatabaseResult.Failure(string.Format("Database '{0}' is unknown.", project.Database));

                IConnectionFactory connectionFactory = new SafeConnectionFactory((IConnectionFactory)_kernel.Resolve(project.Database.ToConnectionFactoryKey(), typeof(IConnectionFactory)));

                try
                {
                    using (IDbConnection connection = connectionFactory.CreateConnection(connectionString))
                    {
                        connection.Open();

                        CheckingDatabaseResult result = _checkDatabaseTask.CheckDatabase(project, connection);

                        if (!result.Failed)
                            transactionScope.Complete();

                        return result;
                    }
                }
                catch (CreatingConnectionException e)
                {
                    return CheckingDatabaseResult.Failure(string.Format("Creating database connection failed ({0})", e.Message));
                }
            }
        }
    }
}